package com.loe.ktui

import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.core.content.res.getBooleanOrThrow
import androidx.core.content.res.getDimensionOrThrow
import androidx.core.content.res.getStringOrThrow
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.flexbox.FlexboxLayout
import com.loe.ktui.util.KtCornerProvider
import com.loe.ktui.util.UiShape

/**
 * Ui基类
 *
 * @author 章路顺
 * @since 2022/9/30-15:27
 */
open class UiView(var id: String? = null)
{
    open var style: Any? = null
        set(value)
        {
            field = value
            if(value != null)
            {
                if (value is List<*>)
                {
                    value.forEach { it2 -> if (it2 != null) addStyle(it2) }
                } else
                {
                    addStyle(value)
                }
            }
        }

    open var width: Any? = null
    open var height: Any? = null
    open var layoutGravity: Int? = null
    open var weight: Double? = null
    open var margin: Any? = null
    open var marginH: Any? = null
    open var marginV: Any? = null
    open var marginLeft: Any? = null
    open var marginTop: Any? = null
    open var marginRight: Any? = null
    open var marginBottom: Any? = null

    open var bg: Any? = null
    open var bgRes: Int? = null
    open var padding: Any? = null
    open var paddingH: Any? = null
    open var paddingV: Any? = null
    open var paddingLeft: Any? = null
    open var paddingTop: Any? = null
    open var paddingRight: Any? = null
    open var paddingBottom: Any? = null
    open var transZ: Any? = null
    open var alpha: Double? = null
    open var scaleX: Double? = null
    open var scaleY: Double? = null
    open var pivotX: Double? = null
    open var pivotY: Double? = null
    open var rotation: Double? = null
    open var visible: Boolean? = null
    open var clickable: Boolean? = null
    open var focus: Boolean? = null
    open var corner: Any? = null
    open var cornerDirect: String? = null
    open var minWidth: Any? = null
    open var minHeight: Any? = null
    open var link: String? = null

    /** gridLayout */
    open var layoutColumn: Int? = null
    open var layoutRow: Int? = null
    open var layoutColumnSpan: Int? = null
    open var layoutRowSpan: Int? = null
    open var layoutColumnWeight: Double? = null
    open var layoutRowWeight: Double? = null

    /** flexLayout */
    open var alignSelf: Int? = null
    open var flexGrow: Double? = null
    open var flexShrink: Double? = null
    open var flexBasisPercent: Double? = null
    open var isWrapBefore: Boolean? = null
    open var maxHeight: Any? = null
    open var maxWidth: Any? = null
    open var order: Int? = null

    /** relativeLayout */
    open var centerHorizontal: Boolean? = null
    open var centerVertical: Boolean? = null
    open var centerInParent: Boolean? = null
    open var alignParentLeft: Boolean? = null
    open var alignParentTop: Boolean? = null
    open var alignParentRight: Boolean? = null
    open var alignParentBottom: Boolean? = null
    open var above: String? = null
    open var below: String? = null
    open var toLeftOf: String? = null
    open var toRightOf: String? = null
    open var alignLeft: String? = null
    open var alignTop: String? = null
    open var alignRight: String? = null
    open var alignBottom: String? = null

    open var isAlphaClick: Boolean? = null

    open var onClick: (View.() -> Unit)? = null
    open fun onClick(onClick: View.() -> Unit)
    {
        this.onClick = onClick
    }

    open var delayClick: (View.() -> Unit)? = null
    open fun delayClick(delayClick: View.() -> Unit)
    {
        this.delayClick = delayClick
    }

    open var onCreateView: (View.() -> Unit)? = null
    open fun onCreateView(onCreateView: (View.() -> Unit))
    {
        this.onCreateView = onCreateView
    }

    /** 添加到父元素 */
    fun addToParent(parent: ViewGroup?, view: View)
    {
        onCreateView?.let { on -> view.on() }
        if (parent == null) return

        var lParams: ViewGroup.LayoutParams? = null
        val margins: Array<Int>
        if (margin != null)
        {
            val m = margin.dist
            margins = arrayOf(m, m, m, m)
        } else
        {
            margins = arrayOf(0, 0, 0, 0)
            if (marginH != null)
            {
                val mH = marginH.dist
                margins[0] = mH
                margins[2] = mH
            }
            if (marginV != null)
            {
                val mV = marginV.dist
                margins[1] = mV
                margins[3] = mV
            }
            if (margins[0] == 0) margins[0] = marginLeft.dist
            if (margins[1] == 0) margins[1] = marginTop.dist
            if (margins[2] == 0) margins[2] = marginRight.dist
            if (margins[3] == 0) margins[3] = marginBottom.dist
        }

        width = width ?: WRAP_CONTENT
        height = height ?: WRAP_CONTENT

        when (parent)
        {
            is FrameLayout ->
            {
                val params = FrameLayout.LayoutParams(width.dist, height.dist)
                layoutGravity?.let { params.gravity = it }
                params.setMargins(margins[0], margins[1], margins[2], margins[3])
                lParams = params
            }
            is LinearLayout ->
            {
                val params = LinearLayout.LayoutParams(width.dist, height.dist)
                layoutGravity?.let { params.gravity = it }
                weight?.let { params.weight = it.toFloat() }
                params.setMargins(margins[0], margins[1], margins[2], margins[3])
                lParams = params
            }
            is RelativeLayout ->
            {
                val params = RelativeLayout.LayoutParams(width.dist, height.dist)
                params.setMargins(margins[0], margins[1], margins[2], margins[3])

                centerHorizontal?.let { params.addRule(RelativeLayout.CENTER_HORIZONTAL, if (it) -1 else 0) }
                centerVertical?.let { params.addRule(RelativeLayout.CENTER_VERTICAL, if (it) -1 else 0) }
                centerInParent?.let { params.addRule(RelativeLayout.CENTER_IN_PARENT, if (it) -1 else 0) }
                alignParentLeft?.let { params.addRule(RelativeLayout.ALIGN_PARENT_LEFT, if (it) -1 else 0) }
                alignParentTop?.let { params.addRule(RelativeLayout.ALIGN_PARENT_TOP, if (it) -1 else 0) }
                alignParentRight?.let { params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, if (it) -1 else 0) }
                alignParentBottom?.let { params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, if (it) -1 else 0) }
                above?.let { params.addRule(RelativeLayout.ABOVE, KtUi.getId(it)) }
                below?.let { params.addRule(RelativeLayout.BELOW, KtUi.getId(it)) }
                toLeftOf?.let { params.addRule(RelativeLayout.LEFT_OF, KtUi.getId(it)) }
                toRightOf?.let { params.addRule(RelativeLayout.RIGHT_OF, KtUi.getId(it)) }
                alignLeft?.let { params.addRule(RelativeLayout.ALIGN_LEFT, KtUi.getId(it)) }
                alignTop?.let { params.addRule(RelativeLayout.ALIGN_TOP, KtUi.getId(it)) }
                alignRight?.let { params.addRule(RelativeLayout.ALIGN_RIGHT, KtUi.getId(it)) }
                alignBottom?.let { params.addRule(RelativeLayout.ALIGN_BOTTOM, KtUi.getId(it)) }

                view.layoutParams = params

                lParams = params
            }
            is GridLayout ->
            {
                val params = GridLayout.LayoutParams()
                params.width = width.dist
                params.height = height.dist
                params.setMargins(margins[0], margins[1], margins[2], margins[3])
                params.columnSpec = GridLayout.spec(
                    layoutColumn ?: GridLayout.UNDEFINED,
                    layoutColumnSpan ?: 1,
                    (layoutColumnWeight ?: 0).toFloat()
                )
                params.rowSpec = GridLayout.spec(
                    layoutRow ?: GridLayout.UNDEFINED,
                    layoutRowSpan ?: 1,
                    (layoutRowWeight ?: 0).toFloat()
                )
                layoutGravity?.let { params.setGravity(it) }
                lParams = params
            }
            is FlexboxLayout ->
            {
                val params = FlexboxLayout.LayoutParams(width.dist, height.dist)
                params.setMargins(margins[0], margins[1], margins[2], margins[3])
                alignSelf?.let { params.alignSelf = it }
                flexGrow?.let { params.flexGrow = it.toFloat() }
                flexShrink?.let { params.flexShrink = it.toFloat() }
                flexBasisPercent?.let { params.flexBasisPercent = it.toFloat() }
                isWrapBefore?.let { params.isWrapBefore = it }
                maxHeight?.let { params.maxHeight = it.dist }
                maxWidth?.let { params.maxWidth = it.dist }
                minHeight?.let { params.minHeight = it.dist }
                minWidth?.let { params.minWidth = it.dist }
                order?.let { params.order = it }
                lParams = params
            }
            is DrawerLayout ->
            {
                val params = DrawerLayout.LayoutParams(width.dist, height.dist)
                layoutGravity?.let { params.gravity = it }
                params.setMargins(margins[0], margins[1], margins[2], margins[3])
                lParams = params
                view.isEnabled = true
            }
            else ->
            {
                val params = ViewGroup.MarginLayoutParams(width.dist, height.dist)
                params.setMargins(margins[0], margins[1], margins[2], margins[3])
                lParams = params
            }
        }
        parent.addView(view, lParams)
    }

    open fun initUI(context: Context, view: View)
    {
        id?.let { KtUi.addId(it, view) }
        bg?.let {
            if (it is UiShape)
            {
                view.background = it.createDrawable()
            } else if (it is Int)
            {
                view.setBackgroundColor(it)
            } else
            {
                view.bg = it.toString()
            }
        }
        bgRes?.let { view.setBackgroundResource(it) }
        if (padding != null)
        {
            val p = padding.dist
            view.setPadding(p, p, p, p)
        } else
        {
            val p = arrayOf(0, 0, 0, 0)
            if (paddingH != null)
            {
                val pH = paddingH.dist
                p[0] = pH
                p[2] = pH
            }
            if (paddingV != null)
            {
                val pV = paddingV.dist
                p[1] = pV
                p[3] = pV
            }
            if (p[0] == 0) p[0] = paddingLeft.dist
            if (p[1] == 0) p[1] = paddingTop.dist
            if (p[2] == 0) p[2] = paddingRight.dist
            if (p[3] == 0) p[3] = paddingBottom.dist
            view.setPadding(p[0], p[1], p[2], p[3])
        }
        transZ?.let { view.translationZ = it.distFloat }
        alpha?.let { view.alpha = it.toFloat() }
        scaleX?.let { view.scaleX = it.toFloat() }
        scaleY?.let { view.scaleY = it.toFloat() }
        pivotX?.let { view.pivotX = it.toFloat() }
        pivotY?.let { view.pivotY = it.toFloat() }
        rotation?.let { view.rotation = it.toFloat() }
        visible?.let { view.visibility = if (it) View.VISIBLE else View.GONE }
        clickable?.let { view.isClickable = it }
        focus?.let {
            view.isFocusableInTouchMode = it
            view.isFocusable = it
            if (it && view is EditText)
            {
                view.clearFocus()
                view.requestFocus()
                view.setSelection(view.length())
            }
        }
        corner?.let {
            view.outlineProvider = KtCornerProvider(corner.dist, cornerDirect ?: "", view)
        }
        minWidth?.let { view.minimumWidth = it.dist }
        minHeight?.let { view.minimumHeight = it.dist }
        link?.let { link -> view.setKtDelayClickListener { KtUi.onLink?.invoke(context, link) } }

        onClick?.let { onClick -> view.setOnClickListener { view.onClick() } }
        delayClick?.let { delayClick -> view.setKtDelayClickListener { view.delayClick() } }

        if (view is KtView) isAlphaClick?.let { view.isAlphaClick = it }
    }

    open fun createView(context: Context, parent: ViewGroup? = null): View
    {
        return KtView(context).also()
        {
            initUI(context, it)
            addToParent(parent, it)
        }
    }

    fun addStyle(ui: Any)
    {
        ktSafe()
        {
            var cls: Class<*>? = ui.javaClass
            while (cls != null)
            {
                cls.declaredFields.forEach()
                { field ->
                    field.isAccessible = true
                    val value = field.get(ui)
                    if (value != null) field.set(this, value)
                }
                cls = cls.superclass
            }
        }
    }
}

fun Any.ui_View(id: String? = null, block: (UiView.() -> Unit)? = null): UiView
{
    val ui = UiView(id)
    block?.let { ui.it() }
    if (this is UiGroup) addChild(ui)
    return ui
}

open class KtView : View
{
    constructor(context: Context) : super(context)
    {
        initXML()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
    {
        initXML(context.obtainStyledAttributes(attrs, R.styleable.KtView))
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    {
        initXML(context.obtainStyledAttributes(attrs, R.styleable.KtView, defStyleAttr, 0))
    }

    private fun initXML(typedArray: TypedArray? = null)
    {
        typedArray?.let()
        {
            if (it.hasValue(R.styleable.KtView_kt_corner))
            {
                val corner = it.getDimensionOrThrow(R.styleable.KtView_kt_corner).toInt()
                val cornerDirect = KtDirects[it.getInt(R.styleable.KtView_kt_cornerDirect, 4)]

                outlineProvider = KtCornerProvider(corner, cornerDirect, this)
            }
            if (it.hasValue(R.styleable.KtView_kt_isAlphaClick))
            {
                isAlphaClick = it.getBooleanOrThrow(R.styleable.KtView_kt_isAlphaClick)
            }
            if (it.hasValue(R.styleable.KtView_kt_link))
            {
                link = it.getStringOrThrow(R.styleable.KtView_kt_link)
            }

            it.recycle()
        }
    }

    /********************************************** 点击 ****************************************/

    var link = ""
        set(value)
        {
            field = value
            setKtDelayClickListener { KtUi.onLink?.invoke(context, value) }
        }

    var isAlphaClick = false
        set(value)
        {
            field = value
            isClickable = value
        }

    override fun dispatchSetPressed(pressed: Boolean)
    {
        if (isAlphaClick) alpha = if (pressed || !isEnabled) 0.75f else 1f
        super.dispatchSetPressed(pressed)
    }

    override fun setEnabled(enabled: Boolean)
    {
        if (isAlphaClick) alpha = if (enabled) 1f else 0.75f
        super.setEnabled(enabled)
    }
}